#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>

#include "LOGC.h"

static int nrbridge( char *nnbridge_ip , int connect_nnbridge_port , char *samba_ip , int connect_samba_port )
{
	int			nnbridge_sock ;
	struct sockaddr_in	nnbridge_addr ;
	socklen_t		addr_len = sizeof(struct sockaddr_in) ;
	
	int			samba_connect_flag = 0 ;
	int			samba_sock = 0 ;
	struct sockaddr_in	samba_addr ;
	
	fd_set			fds ;
	int			max_fd ;
	
	int			readlen ;
	char			buffer[ 4096 + 1 ] ;
	int			writelen ;
	
	int			nret = 0 ;
	
	nnbridge_sock = socket( AF_INET , SOCK_STREAM , IPPROTO_TCP );
	if( nnbridge_sock < 0 )
	{
		ErrorLog( __FILE__ , __LINE__ , "socket failed[%d]errno[%d]" , nnbridge_sock , errno );
		return -1;
	}
	
	memset( & nnbridge_addr , 0x00 , sizeof(struct sockaddr_in) );
	nnbridge_addr.sin_family = AF_INET ;
	nnbridge_addr.sin_addr.s_addr = inet_addr( nnbridge_ip ) ;
	nnbridge_addr.sin_port = htons( (unsigned short)connect_nnbridge_port );
	
	nret = connect( nnbridge_sock , ( struct sockaddr *) & nnbridge_addr , addr_len );
	if( nret < 0 )
	{
		ErrorLog( __FILE__ , __LINE__ , "connect failed[%d]errno[%d]" , nret , errno );
		return -1;
	}
	else
	{
		InfoLog( __FILE__ , __LINE__ , "connect nnbridge ok" );
	}
	
	samba_connect_flag = 0 ;
	while(1)
	{
		FD_ZERO( & fds );
		FD_SET( nnbridge_sock , & fds );
		max_fd = nnbridge_sock ;
		if( samba_connect_flag == 1 )
		{
			FD_SET( samba_sock , & fds );
			if( samba_sock > max_fd )
				max_fd = samba_sock ;
		}
		
		nret = select( max_fd + 1 , & fds , NULL , NULL , NULL ) ;
		if( nret == -1 )
		{
			ErrorLog( __FILE__ , __LINE__ , "select failed[%d]errno[%d]" , nret , errno );
			return -1;
		}
		
		if( samba_connect_flag == 1 && FD_ISSET( samba_sock , & fds ) )
		{
			memset( buffer , 0x00 , sizeof(buffer) );
			readlen = (int)read( samba_sock , buffer , sizeof(buffer)-1 ) ;
			if( readlen == -1 )
			{
				ErrorLog( __FILE__ , __LINE__ , "read samba_sock failed[%d]errno[%d]" , readlen , errno );
				return -1;
			}
			else if( readlen == 0 )
			{
				InfoLog( __FILE__ , __LINE__ , "samba_sock socket close" );
				break;
			}
			
			writelen = write( nnbridge_sock , buffer , readlen ) ;
			if( writelen == -1 )
			{
				ErrorLog( __FILE__ , __LINE__ , "write nnbridge_sock failed[%d]errno[%d]" , writelen , errno );
				return -1;
			}
			else if( writelen != readlen )
			{
				ErrorLog( __FILE__ , __LINE__ , "writelen[%d]bytes nnbridge_sock not match with readlen[%d]bytes" , writelen , readlen );
				break;
			}
			else
			{
				InfoLog( __FILE__ , __LINE__ , "samba_sock -> nnbridge_sock [%d]bytes" , writelen );
			}
		}
		
		if( FD_ISSET( nnbridge_sock , & fds ) )
		{
			if( samba_connect_flag == 0 )
			{
				memset( buffer , 0x00 , sizeof(buffer) );
				readlen = (int)read( nnbridge_sock , buffer , 1 ) ;
				if( readlen == -1 )
				{
					ErrorLog( __FILE__ , __LINE__ , "read nnbridge_sock failed[%d]errno[%d]" , readlen , errno );
					return -1;
				}
				else if( readlen == 0 )
				{
					InfoLog( __FILE__ , __LINE__ , "nnbridge socket close" );
					break;
				}
				else
				{
					InfoLog( __FILE__ , __LINE__ , "nnbridge -> nnbridge_sock [%d]bytes" , readlen );
				}
				
				samba_sock = socket( AF_INET , SOCK_STREAM , IPPROTO_TCP );
				if( samba_sock < 0 )
				{
					ErrorLog( __FILE__ , __LINE__ , "socket failed[%d]errno[%d]" , samba_sock , errno );
					return -1;
				}
				
				memset( & samba_addr , 0x00 , sizeof(struct sockaddr_in) );
				samba_addr.sin_family = AF_INET ;
				samba_addr.sin_addr.s_addr = inet_addr( samba_ip ) ;
				samba_addr.sin_port = htons( (unsigned short)connect_samba_port );
				
				nret = connect( samba_sock , ( struct sockaddr *) & samba_addr , addr_len );
				if( nret < 0 )
				{
					ErrorLog( __FILE__ , __LINE__ , "connect failed[%d]errno[%d]" , nret , errno );
					return -1;
				}
				else
				{
					InfoLog( __FILE__ , __LINE__ , "connect samba ok" );
				}
				
				samba_connect_flag = 1 ;
				
				continue;
			}
			
			memset( buffer , 0x00 , sizeof(buffer) );
			readlen = (int)read( nnbridge_sock , buffer , sizeof(buffer)-1 ) ;
			if( readlen == -1 )
			{
				ErrorLog( __FILE__ , __LINE__ , "read nnbridge_sock failed[%d]errno[%d]" , readlen , errno );
				return -1;
			}
			else if( readlen == 0 )
			{
				InfoLog( __FILE__ , __LINE__ , "nnbridge socket close" );
				break;
			}
			
			writelen = write( samba_sock , buffer , readlen ) ;
			if( writelen == -1 )
			{
				ErrorLog( __FILE__ , __LINE__ , "write samba_sock failed[%d]errno[%d]" , writelen , errno );
				return -1;
			}
			else if( writelen != readlen )
			{
				ErrorLog( __FILE__ , __LINE__ , "writelen[%d]bytes samba_sock not match with readlen[%d]bytes" , writelen , readlen );
				break;
			}
			else
			{
				InfoLog( __FILE__ , __LINE__ , "nnbridge_sock -> samba_sock [%d]bytes" , writelen );
			}
		}
	}
	
	close( nnbridge_sock );
	close( samba_sock );
	
	return 0;
}

static void usage()
{
	printf( "nrbridge v0.1.0\n" );
	printf( "USAGE : nrbridge nnbridge_ip connect_nnbridge_port samba_ip connect_samba_port\n" );
	return;
}

int main( int argc , char *argv[] )
{
	if( argc == 1 + 4 )
	{
		// SetLogFile( "%s/log/nrbridge.log" , getenv("HOME") );
		SetLogLevel( LOGLEVEL_DEBUG );
		return -nrbridge( argv[1] , atoi(argv[2]) , argv[3] , atoi(argv[4]) );
	}
	else
	{
		usage();
		return 9;
	}
}
